#include <assert.h>
#include "network_service_impl.h"
#include "network_base.h"
#include "network_tcp.h"
#include <core/QbLogger.h>

namespace qb
{
	CNetworkServiceImpl::CNetworkServiceImpl()
		: m_last_connect_id(-1)
		, m_critSect(*CriticalSectionWrapper::CreateCriticalSection())
	{
	}

	CNetworkServiceImpl::~CNetworkServiceImpl()
	{
		RemoveObserver<INetworkEventObserver>(m_observers);

		assert(m_sockets.size() == 0);
		if (m_sockets.size() > 0)
		{
			CriticalSectionScoped cs(&m_critSect);
			for (std::map<int32_t,CSocketBase*>::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it)
			{
				it->second->Disconnect();
				delete it->second;
			}
			m_sockets.clear();
		}

		delete &m_critSect;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::DisconnectAll(THIS_)
	{
		int32_t ret = 0;
		CriticalSectionScoped cs(&m_critSect);
		for (std::map<int32_t,CSocketBase*>::iterator it = m_sockets.begin();
			it != m_sockets.end(); ++it)
		{
			if (it->second->Disconnect() != 0)
			{
				log_error("disconnect failed id is %d",it->first);
				ret = -1;
			}
		}

		return ret;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::CreateSocket(THIS_ SocketType sock_type)
	{
		log_info("%s",__FUNCTION__);

		CSocketBase* base = NULL;
		if (sock_type == kSocketTcp)
		{
			base = new CSocketTcp(sock_type);
		}
		else if (sock_type == kSocketUdp)
		{
			//base = new CSocketUdp(sock_type);
			return -1;
		}
		else
		{
			assert(0);
			return -1;
		}

		CriticalSectionScoped cs(&m_critSect);
		m_sockets[++m_last_connect_id] = base;
		base->InitlizeSocket(m_last_connect_id.Value());

		return m_last_connect_id.Value();
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::DestorySocket(THIS_ int32_t connect_id)
	{
		log_info("(%d)", connect_id);

		if (connect_id < 0)
		{
			return -1;
		}

		CriticalSectionScoped cs(&m_critSect);
		std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
		if (it == m_sockets.end())
		{
			log_error("(%d) can not find socket", connect_id);
			return -1;
		}

		m_sockets.erase(it);

		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::Connect(THIS_ int32_t connect_id, const char* ipAddress, uint16_t port)
	{
		log_info("Start");

		if (connect_id < 0)
		{
			return -1;
		}

		CSocketBase* base = NULL;
		{
			CriticalSectionScoped cs(&m_critSect);
			std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
			if (it == m_sockets.end())
			{
				log_error("(%d) can not find socket", connect_id);
				return -1;
			}
			base = it->second;
		}

		return base->Connect(ipAddress, port);
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::Disconnect(int32_t connect_id)
	{
		if (connect_id < 0)
		{
			return -1;
		}

		log_info("begin");

		CSocketBase* base = NULL;
		{
			CriticalSectionScoped cs(&m_critSect);
			std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
			if (it == m_sockets.end())
			{
				log_error("(% d) can not find socket", connect_id);
				return -1;
			}
			base = it->second;
		}

		return base->Disconnect();
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::Send(THIS_ int32_t connect_id, const char* buff, uint32_t bytes)
	{
		if (connect_id < 0)
		{
			return -1;
		}

		CSocketBase* base = NULL;
		{
			CriticalSectionScoped cs(&m_critSect);
			std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
			if (it == m_sockets.end())
			{
				log_error("(%d) can not find socket", connect_id);
				return -1;
			}
			base = it->second;
		}

		return base->Send(buff,bytes);
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::NotifyForceQuit(THIS_ int32_t connect_id)
	{
		BList<INetworkEventObserver*>::iterator iter = m_observers.begin();
		for_each_list(m_observers, iter, (iter++)->first->OnForceQuit(connect_id));

		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::NotifyDisconnect(THIS_ int32_t connect_id)
	{
		BList<INetworkEventObserver*>::iterator iter = m_observers.begin();
		for_each_list(m_observers, iter, (iter++)->first->OnDisConnect(connect_id));

		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::NotifyRecover(THIS_ int32_t connect_id)
	{
		BList<INetworkEventObserver*>::iterator iter = m_observers.begin();
		for_each_list(m_observers, iter, (iter++)->first->OnRecover(connect_id));

		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::RegisterNetworkServiceObserver(THIS_ int32_t connect_id, INetworkServiceObserver* observer)
	{
		if (connect_id < 0)
		{
			return -1;
		}

		CriticalSectionScoped cs(&m_critSect);
		std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
		if (it == m_sockets.end())
		{
			log_error("(%d) can not find socket", connect_id);
			return -1;
		}

		return it->second->RegisterSocketObserver(observer);
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::DeRegisterNetworkServiceObserver(THIS_ int32_t connect_id, INetworkServiceObserver* observer)
	{
		if (connect_id < 0)
		{
			return -1;
		}

		CriticalSectionScoped cs(&m_critSect);
		std::map<int32_t, CSocketBase*>::iterator it = m_sockets.find(connect_id);
		if (it == m_sockets.end())
		{
			log_error("(%d) can not find socket", connect_id);
			return -1;
		}

		return it->second->DeRegisterSocketObserver(observer);
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::RegisterNetworkEventObserver(THIS_ INetworkEventObserver* observer)
	{
		RegisterObserver<INetworkEventObserver>(observer, m_observers);
		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::DeRegisterNetworkEventObserver(THIS_ INetworkEventObserver* observer)
	{
		DeRegisterObserver<INetworkEventObserver>(observer, m_observers);
		return 0;
	}

	STDMETHODIMP_(int32_t) CNetworkServiceImpl::SetProxy(THIS_ 
		int32_t proxy_type,
		const char* proxy_host,
		uint16_t proxy_port,
		const char* username,
		const char* password,
		const char* domain)
	{
        if (proxy_host)
        {
            log_debug("proxy ip: %s, port: %u", 
                 proxy_host, proxy_port);
        }        

		CSocketProxy::SetGlobalProxy(
			(ProxyType)proxy_type,
			proxy_host,
			proxy_port,
			username,
			password,
			domain);

		return 0;
    }

    STDMETHODIMP_(int32_t) CNetworkServiceImpl::ProxyTest(THIS_
        int32_t proxy_type,
        const char* dst_host,
        uint16_t dst_port,
        const char* proxy_host,
        uint16_t proxy_port,
        const char* username,
        const char* password,
        const char* domain)
    {
        return CSocketProxy::Test((ProxyType)proxy_type,
                                    dst_host,
                                    dst_port,
                                    proxy_host,
                                    proxy_port,
                                    username,
                                    password,
                                    domain);
    }

	STDMETHODIMP_(uint32_t) CNetworkServiceImpl::GetTargetIP(THIS_ int32_t connect_id)
	{
		assert(connect_id > -1);
		CriticalSectionScoped cs(&m_critSect);
		std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
		if (it == m_sockets.end())
		{
			log_error("(%d) can not find socket", connect_id);
			return 0;
		}

		return it->second->GetTargetIP();
	}

	STDMETHODIMP_(uint16_t) CNetworkServiceImpl::GetTargetPort(THIS_ int32_t connect_id)
	{
		assert(connect_id > -1);
		CriticalSectionScoped cs(&m_critSect);
		std::map<int32_t,CSocketBase*>::iterator it = m_sockets.find(connect_id);
		if (it == m_sockets.end())
		{
			log_error("(%d) can not find socket", connect_id);
			return 0;
		}

		return it->second->GetTargetPort();
	}

}
